package net.sf.midollo.net.http.text;

import java.io.EOFException;
import java.io.IOException;

import net.sf.midollo.io.text.Parser;
import net.sf.midollo.utils.List;
import net.sf.midollo.utils.Utils;

public class HttpParser {

  public static final int STRING_BUFFER_LENGTH = 1024;
  public static final int MAX_OPTIONAL_SPACES = 256;

  protected final StringBuffer stringBuffer;
  protected final List headerNameList;
  protected final List headerValueList;
  protected final Parser parser;
  protected int majorVer;
  protected int minorVer;
  protected int statusCode;
  protected String method;
  protected String uri;

  public HttpParser(Parser parser) throws IOException {
    this.stringBuffer = new StringBuffer(STRING_BUFFER_LENGTH);
    this.headerNameList = Utils.newList(8);
    this.headerValueList = Utils.newList(8);
    this.parser = parser;
    this.majorVer = 0;
    this.minorVer = 0;
    this.statusCode = 0;
    this.method = null;
    this.uri = null;
  }

  public int getMajorVer() {
    return this.majorVer;
  }

  public int getMinorVer() {
    return this.minorVer;
  }

  public int getStatusCode() {
    return this.statusCode;
  }

  public String getMethod() {
    return this.method;
  }

  public String getURI() {
    return this.uri;
  }

  public String getHeaderValue(int idx) {
    return (String)this.headerValueList.get(idx);
  }

  public long getHeaderValueLong(int idx) throws NumberFormatException {
    return Long.parseLong(getHeaderValue(idx));
  }

  public int registerHeaderName(String headerName) {
    int idx = this.headerNameList.size();
    headerName = headerName.toLowerCase();
    char[] headerNameChars = headerName.toCharArray();
    this.headerNameList.add(headerNameChars);
    this.headerValueList.add(null);
    return idx;
  }

  public void parseResponse() throws IOException {
    int c = this.parser.peek();
    if (c < 0)
      throw new EOFException();
    resetHeaderValueList();
    parseResponseLine();
    while (parseHeaderLine()) {
    }
    this.parser.parseCRLF();
  }

  public void parseRequest() throws IOException {
    int c = this.parser.peek();
    if (c < 0)
      throw new EOFException();
    resetHeaderValueList();
    parseRequestLine();
    while (parseHeaderLine()) {
    }
    this.parser.parseCRLF();
  }

  public void parseResponseLine() throws IOException {
    this.parser.parseByte('H');
    this.parser.parseByte('T');
    this.parser.parseByte('T');
    this.parser.parseByte('P');
    this.parser.parseByte('/');
    this.majorVer = this.parser.parseInt();
    this.parser.parseByte('.');
    this.minorVer = this.parser.parseInt();
    this.parser.parseSpace();
    this.statusCode = this.parser.parseInt();
    this.parser.parseSpace();
    this.parser.parseString(this.stringBuffer, -1);
    this.parser.parseCRLF();
  }

  public void parseRequestLine() throws IOException {
    this.parser.parseString(this.stringBuffer, ' ');
    this.method = new String(this.stringBuffer);
    this.parser.parseSpace();
    this.parser.parseString(this.stringBuffer, ' ');
    this.uri = new String(this.stringBuffer);
    this.parser.parseSpace();
    this.parser.parseByte('H');
    this.parser.parseByte('T');
    this.parser.parseByte('T');
    this.parser.parseByte('P');
    this.parser.parseByte('/');
    this.majorVer = this.parser.parseInt();
    this.parser.parseByte('.');
    this.minorVer = this.parser.parseInt();
    this.parser.parseCRLF();
  }

  public boolean parseHeaderLine() throws IOException {
    int c = this.parser.peek();
    if (c < 0 || c == '\r' || c == '\n')
      return false;
    this.parser.parseString(this.stringBuffer, ':');
    int idx = indexOfHeaderName();
    this.parser.parseByte(':');
    this.parser.parseOptionalSpaces(MAX_OPTIONAL_SPACES);
    this.parser.parseString(this.stringBuffer, -1);
    if (idx >= 0)
      setHeaderValue(idx);
    this.parser.parseCRLF();
    return true;
  }

  public void throwError(String msg) throws IOException {
    this.parser.throwError(new IOException("HTTP error: " + msg));
  }

  protected void resetHeaderValueList() {
    for (int idx = 0; idx < this.headerValueList.size(); ++idx)
      this.headerValueList.set(idx, null);
  }

  protected int indexOfHeaderName() {
    for (int idx = 0; idx < this.headerNameList.size(); ++idx) {
      char[] headerName = (char[])this.headerNameList.get(idx);
      if (equalsHeaderName(headerName))
        return idx;
    }
    return -1;
  }

  protected void setHeaderValue(int idx) {
    this.headerValueList.set(idx, new String(this.stringBuffer));
  }

  protected boolean equalsHeaderName(char[] headerName) {
    char c1;
    char c2;
    if (headerName.length != this.stringBuffer.length())
      return false;
    for (int idx = 0; idx < headerName.length; ++idx) {
      c1 = headerName[idx];
      c2 = Character.toLowerCase(this.stringBuffer.charAt(idx));
      if (c1 != c2)
        return false;
    }
    return true;
  }
}
